package bosVM2.meta;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.Set;

import bosVM2.FaultHandling.StringEncoderDecoder;
import bosVM2.meta.Objects.BosClass;
import bosVM2.meta.Objects.BosMethod;
import bosVM2.util.Map;

public class MetaData {	
	
	//faulthandling
	StringEncoderDecoder ham = new StringEncoderDecoder();

	//Class, method, field, and array meta data.
	private Map<String, BosClass> classes = new Map<String,BosClass>();
	
	//Classes
	
	//in-Function
	/*
	 * Add a meta class
	 * @param b BosClass object
	 */
	public void addMetaClass(BosClass b) {	
		String name = ham.enCode(b.getClassName());
		classes.put(name, b);
	}
	
	//out-function
	/*
	 * Retrieve the meta data of class b
	 * @param b Class name.
	 * @return The BosClass object identified by param b.
	 */
	public BosClass getMetaClass(String b) {
		String name = ham.enCode(b);
		BosClass out = classes.get(name);
		if(out == null) {
			if(mapKeyErrorCheckAndCorrection()) {
				out = classes.get(name);
			} else { return null; } //TODO cast internal exception?
		}
		return out;
	}
	
	//out function
	/*
	 * Checks if a class i known in the meta data already.
	 * @param clss The class name.
	 * @return true or false.
	 */
	public boolean classKnown(String clss) {
		String coded = ham.enCode(clss);
		if(classes.containsKey(coded)) { return true; }
		else {
			if(mapKeyErrorCheckAndCorrection()) {
				if( classes.containsKey(coded)) { 
					return true; }
				else { return false; } 
				}
			else { return false; }
			}
	}
	
	/*
	 * retrieves a set of known classes.
	 * error detects and corrects.
	 */
	public Set<String> getClasses() {
		ArrayList<Object[]> error = null;
		HashSet<String> out = new HashSet<String>();
		for(String s: classes.getKeys()) {
			Object[] temp = ham.deCode(s);
			if((boolean) temp[0]) {
				if(error == null) {
						error = new ArrayList<Object[]>();
					}
				error.add(temp);
			}
			out.add((String) ham.deCode(s)[1]);
		}
		if(error != null) {
			for(Object[] o: error) {
				mapKeyErrorCorrection(o);
			}
		}
		return out;
	}
	
	public Set<String> getClassesCoded() {
		return classes.getKeys();
	}
	
	/*
	 * Replaces a key with another key, identified by the deCode function.
	 * @param errorArray index 0: boolean- not used
	 * 					 index 1: String- Error corrected string
	 * 					 index 2: String- Encoded bit-flipped string.
	 */
	private void mapKeyErrorCorrection(Object[] errorArray) {
		classes.replaceKey((String) errorArray[2], ham.enCode((String) errorArray[1])); 
	}
	/*
	 * A function for finding and correction error in the keys of the classes map.
	 * @return true if an error has been found and corrected, false if not.
	 */
	private boolean mapKeyErrorCheckAndCorrection() {
		ArrayList<Object[]> error = null;
		for(String s: classes.getKeys()) {
			Object[] temp = ham.deCode(s);
			if((boolean) temp[0]) {
				if(error == null) {
						error = new ArrayList<Object[]>();
					}
				error.add(temp);
			}
		}
		if(error != null) {
			for(Object[] o: error) {
				mapKeyErrorCorrection(o);
			}
			return true;
		} else { return false; }
	}
	
	//Methods
	
	//get Meta data of a method
	public BosMethod getMetaMethod(String classname,String method) {
		String cl = ham.enCode(classname);
		return new BosMethod(classes.get(cl).getMethod(method));
	}
	
	//get method ins at pc
	public Object[] getMethodInsAtPc(String classname,String method,int pc)	{
		return getMetaMethod(classname,method).instructionAt(pc);
	}
	
	
	public Map<String, BosClass> getClassMap() {
		return classes;
	}
	
	//Util
	public String toString()
	{
		return "MetaData: " + " classes: " + classes;
	}
	
}
